<?php

namespace seecms\db;

use Closure;
use seecms\See;

class SeeDb
{
    /**
     * DB实例列表
     *
     * @var SeeDbConnection[]
     */
    protected static $pool = [];

    /**
     * 事件钩子
     *
     * @var array
     */
    protected static $events = [
        // 链接DB
        'connect' => [],
        // select查询
        'select' => [],
        // insert查询
        'insert' => [],
        // delete查询
        'delete' => [],
        // update查询
        'update' => [],
        // query全局查询
        'query' => [],
        // execute全局指令
        'execute' => [],
        // 开启事务
        'startTrans' => [],
        // 提交事务
        'commitTrans' => [],
        // 回滚事务
        'rollbackTrans' => [],
        // 开启事扩库务
        'startTransXA' => [],
        // 开启预编译XA事务
        'prepareTransXA' => [],
        // 提交跨库事务
        'commitTransXA' => [],
        // 回滚跨库事务
        'rollbackTransXA' => [],
    ];

    /**
     * 数据库配置
     * @var array
     */
    protected static $config = [];

    /**
     * 断开连接是否重连，注意：强制重连有可能导致数据库core掉
     *
     * @var boolean
     */
    protected static $break_reconnect = false;

    /**
     * 链接数据库
     * @param string|array $config 数据库哦配置
     * @param boolean $reset 是否重连
     * @return SeeDbConnection 链接实例
     */
    public static function connect($config = [], bool $reset = false): SeeDbConnection
    {
        // 未定义配置信息，获取默认配置信息
        if (empty($config)) {
            $config = See::config()->database->config;
        }
        // 获取链接池key值
        $key = self::getKey($config);
        // 重连或者不存在链接
        if ($reset === true || !isset(self::$pool[$key])) {
            self::$pool[$key] = new SeeDbConnection($config);
        }

        return self::$pool[$key];
    }

    /**
     * 定义或获取数据库断开是否自动重连
     *
     * @param boolean|null $reconnect
     * @return boolean
     */
    public static function reconnect(bool $reconnect = null): bool
    {
        if (!is_null($reconnect)) {
            self::$break_reconnect = $reconnect;
        }

        return self::$break_reconnect;
    }

    /**
     * 监听事件
     *
     * @param string $event 钩子名称
     * @param mixed $callable 钩子回调
     * @return void
     */
    public static function listen(string $event, callable $callable)
    {
        isset(self::$events[$event]) || self::$events[$event] = [];
        self::$events[$event][] = $callable;
    }

    /**
     * 监听&执行事件回调
     * @param mixed $event 事件名
     * @param SeeDbConnection $connection 链接实例
     * @param mixed &$params 参数
     * @return array
     * @throws SeeDbException
     */
    public static function trigger($event, SeeDbConnection $connection, $params = []): array
    {
        $result = [];

        if (isset(self::$events[$event])) {
            $callbacks = self::$events[$event];
            foreach ($callbacks as $k => $callback) {
                if (is_string($callback) && $callback) {
                    $res = call_user_func_array([$callback, 'handler'], [$connection, $params]);
                } elseif ($callback instanceof Closure) {
                    $res = call_user_func($callback, $connection, $params);
                } else {
                    throw new SeeDbException('Event callback failed!', SeeDbException::EVENT_CALLBACK_FAILED);
                }

                // 如果返回false 则中断事件执行
                if (false === $res) {
                    break;
                }

                $result[$k] = $res;
            }
        }

        return $result;
    }

    /**
     * 配置对应加密key
     *
     * @param array $config 配置信息
     * @return string 配置key值
     */
    public static function getKey(array $config): string
    {
        return md5(serialize($config));
    }
}
